package slave;

import cn.hutool.core.convert.Convert;
import core.IEC104Constant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.Random;
import java.util.concurrent.TimeUnit;


/**
 * @author 舒克、舒克
 * @date 2025/3/6 16:25
 * @description: 服务端协议处理器
 */
public class IEC104ServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
    // 接收序列号 N(R) ， 记录当前期望接收的I帧序号（范围0~32767）
    private int receiveSeq = 0;
    private int sendSeq = 0; // 发送序列号 N(S)

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 周期发送遥测数据
        ctx.executor().scheduleAtFixedRate(() -> {
            float value = new Random().nextFloat() * 100; // 模拟遥测值
            sendYcData(ctx, value);
        }, 0, 20, TimeUnit.SECONDS);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)  {
        try{
            // 1. 读取接收到的字节数据,将ByteBuf转换为字节数组
            byte[] data = new byte[msg.readableBytes()];
            msg.readBytes(data);

            // 2. 解析控制域（第三字节，从0开始），判断帧类型
            byte controlByte = data[0];
            if ((controlByte & 0x03) == 0x03) {
                handleUFrame(ctx, data);
            }
            // 2.2 判断帧类型：S帧（最低位为1）
            else if ((controlByte & 0x01) == 0x01) {
                handleSFrame(ctx, data);
            }
            // 2.3 判断帧类型：I帧（最低两位为00）
            else {
                handleIFrame(ctx, data);
            }

        }finally{
            // 释放ByteBuf内存
//            msg.release();  // ❌ SimpleChannelInboundHandler已自动释放！
//            SimpleChannelInboundHandler会自动释放传入的ByteBuf，
//            如果在代码中再次手动释放（比如调用msg.release()），会导致引用计数变为负数
        }
    }

    // 处理U帧（链路控制）
    private void handleUFrame(ChannelHandlerContext ctx, byte[] data) {
        // 3. 解析U帧功能：控制域第3字节为0x07表示STARTDT激活命令
        if (data[2] == 0x07) {
            // 3.1 构造U帧确认报文：68 04 0B 00 00 00
            // 字段解析：
            // 68：起始符（固定）
            // 04：APDU长度（后续4字节）
            // 0B：控制域（U帧确认，0B表示STARTDT确认）
            // 00 00：保留字段
            if (data.length >= 6 && data[2] == 0x07) {
                byte[] resp = new byte[]{0x68, 0x04, 0x0B, 0x00, 0x00, 0x00};
                ctx.writeAndFlush(Unpooled.copiedBuffer(resp));
                System.out.println("服务端回复U帧确认：STARTDT激活成功");
            }
        }
    }

    // 处理S帧（确认接收）
    private void handleSFrame(ChannelHandlerContext ctx, byte[] data) {
        // 4. 解析接收序列号N(R)：S帧的N(R)位于第5-6字节（小端序）
        // 注意：S帧的N(R)表示接收方已正确接收的I帧序号（期望下次接收N(R)）
        int ackSeq = ((data[4] & 0xFF) >> 1) | ((data[5] & 0xFF) << 7);
        System.out.println("服务端收到S帧确认，接收序列号N(R)=" + ackSeq);
    }

    // 处理I帧（应用数据）
    private void handleIFrame(ChannelHandlerContext ctx, byte[] data) {
        // 5. 更新接收序列号N(R)：每接收一个I帧，N(R)递增（模32768）
        receiveSeq = (receiveSeq + 1) % 32768;

        // 6. 构造S帧确认报文：68 04 01 00 [N(R)] 00
        // 字段解析：
        // 01：控制域（S帧标识，最低位为1）
        // 00：保留字段
        // [N(R)]：接收序列号（低7位在字节4，高8位在字节5）
        byte[] sFrame = new byte[]{
                0x68, 0x04,
                0x01, 0x00,
                (byte) ((receiveSeq << 1) & 0xFF), // 低7位左移1位（空出最低位）
                (byte) ((receiveSeq >> 7) & 0xFF)    // 高8位直接右移7位
        };
        ctx.writeAndFlush(Unpooled.copiedBuffer(sFrame));
        System.out.println("服务端收到I帧，回复S帧确认，N(R)=" + receiveSeq);
    }

    /**
     *  发送 遥测 数据
     */
    private void sendYcData(ChannelHandlerContext ctx, float value) {
        // 归一化值
        short normalizedValue = (short) (value * 32.767);

        // 构造 ASDU 部分
        byte[] asdu = new byte[]{
                (byte) IEC104Constant.ASDU_YC_ME_NA_1, // 类型标识（9）
                0x01,                                  // 可变结构限定词（SQ=0, 信息体数量=1）
                (byte) IEC104Constant.COT_PERIODIC,     // 传送原因（3）
                0x00,
                (byte) IEC104Constant.COMMON_ADDRESS,  // 公共地址
                0x00,
                0x00, 0x00, 0x02,                      // 信息体地址（2）
                (byte) (normalizedValue & 0xFF),            // 归一化值低字节
                (byte) (normalizedValue >> 8),              // 归一化值高字节
                0x00                                   // 品质描述（正常）
        };

        // 构造APDU（I帧）
        byte[] apdu = buildIFrame(asdu);
        ctx.writeAndFlush(Unpooled.copiedBuffer(apdu));
        System.out.println("服务端发送遥测数据: 地址=2，值=" + value + ", N(s) = " + sendSeq);
    }

    // 构造I帧
    private byte[] buildIFrame(byte[] asdu) {
        ByteBuf buf = Unpooled.buffer();
        buf.writeByte(0x68);                          // 起始符
        buf.writeByte(asdu.length + 4);               // 长度字段（控制域4字节 + ASDU长度）
        buf.writeByte((sendSeq << 1) & 0xFF);         // 控制域低字节（发送序号）
        buf.writeByte(sendSeq >> 7);                 // 控制域高字节
        buf.writeByte(0x00);                         // 控制域（接收序号低字节）
        buf.writeByte(0x00);                         // 控制域（接收序号高字节）
        buf.writeBytes(asdu);                        // ASDU部分

        // 更新sendSeq（模32768）
        sendSeq = (sendSeq + 1) % 32768;

        return buf.array();
    }

}
